`include "chunjun_define.sv" 
`include "chunjun_lib_define.sv" 

//================================================================================================
// File Name   : chunjun_tcu_vtcmarb.sv
// Create Time : Wed Nov  6 14:59:06 2024
// Description : Arbiter for VTCM 
//================================================================================================
 
module chunjun_tcu_vtcmarb #(
  parameter ID_W       = 7 ,
  parameter RAM_DATA_W = 32,
  parameter BANK_N     = 2
)(
input  logic                                  clk              ,  //时钟信号
input  logic                                  rst_n            ,  //复位信号
 
// lsu <-> vtcm
input  logic [1:0]                            lsu_vtcm_req_vld , //lsu访问vtcm的请求有效信号
output logic [1:0]                            vtcm_lsu_req_rdy , //vtcm返回给lsu的请求响应信号
input  logic [1:0][19:0]                      lsu_vtcm_req_addr, //lsu访问vtcm的地址
input  logic [1:0][ID_W-1:0]                  lsu_vtcm_req_id  , //lsu访问vtcm的id
 
// sbu <-> vtcm
input  logic                                  sbu_vtcm_req_vld  , //sbu访问vtcm的请求有效信号
output logic                                  vtcm_sbu_req_rdy  , //vtcm返回给sbu的请求响应信号
input  logic [19:0]                           sbu_vtcm_req_addr , //sbu访问vtcm的地址
input  logic [ID_W-1:0]                       sbu_vtcm_req_id   , //sbu访问vtcm的id
input  logic [63:0]                           sbu_vtcm_req_wdata, //sbu访问vtcm的数据
input  logic [ 7:0]                           sbu_vtcm_req_wstrb, //sbu访问vtcm的写使能

// slv <-> vtcm
input  logic                                  slv_vtcm_req_vld  , //slv访问vtcm的请求有效信号
output logic                                  vtcm_slv_req_rdy  , //vtcm返回给slv的请求响应信号
input  logic [19:0]                           slv_vtcm_req_addr , //slv访问vtcm的地址
input  logic [ID_W-1:0]                       slv_vtcm_req_id   , //slv访问vtcm的id
input  logic                                  slv_vtcm_req_wr   , //slv访问vtcm的写使能
input  logic [63:0]                           slv_vtcm_req_wdata, //slv访问vtcm的数据
input  logic [ 7:0]                           slv_vtcm_req_wstrb, //slv访问vtcm的写使能

// vpu <-> vtcm
input  logic [BANK_N-1:0]                     vpu_vtcm_req_vld  , //vpu访问vtcm的请求有效信号
output logic [BANK_N-1:0]                     vtcm_vpu_req_rdy  , //vtcm返回给vpu的请求响应信号
input  logic [BANK_N-1:0]                     vpu_vtcm_req_wr   , //vpu访问vtcm的写使能
input  logic [BANK_N-1:0][ID_W-1:0]           vpu_vtcm_req_id , //vpu访问vtcm的id
input  logic [BANK_N-1:0][19:0]               vpu_vtcm_req_addr , //vpu访问vtcm的地址
input  logic [BANK_N-1:0][31:0]               vpu_vtcm_req_wdata, //vpu访问vtcm的数据
input  logic [BANK_N-1:0][3:0]                vpu_vtcm_req_wstrb, //vpu访问vtcm的写使能

// varb <-> vtcm bank
output logic [BANK_N-1:0]                     vbank_vld  , //vtcm bank的请求有效信号
input  logic [BANK_N-1:0]                     vbank_rdy  , //vtcm bank的请求响应信号
output logic [BANK_N-1:0][19:0]               vbank_addr , //vtcm bank的地址
output logic [BANK_N-1:0][ID_W-1:0]           vbank_id   , //vtcm bank的id
output logic [BANK_N-1:0]                     vbank_wr   , //vtcm bank的写使能
output logic [BANK_N-1:0][RAM_DATA_W-1:0]     vbank_wdata, //vtcm bank的数据
output logic [BANK_N-1:0][3:0]                vbank_wstrb, //vtcm bank的写使能
output logic [BANK_N-1:0][1:0]                vbank_dest, //vtcm bank的目的

input  logic [BANK_N-1:0]                     bank_busy   //vtcm bank的忙信号
);

//=================================================================================
// Arch Description
//=================================================================================

// * ldxb: VTCM load xbar.
//         Assign 2 LSU req (2*32b) to the corresponding bank.
//
// * parb: Pre-Arbiter
//         Including a VTCM v-router, Assign SBU or SLV req (64b) to the corresponding bank.
//
// * vpu req: connect to varb directly 
//
// * varb: VTCM main arbiter.

// -----------      -----------      -----------
// |  ldxb   |      |  parb   |      | vpu_req |
// -----------      -----------      -----------
// 32b|  | 32b           | BANK_N * 32b   | BANK_N * 32b 
//    |  |               |                |
// ---------------------------------------------
// |                   varb                    |
// ---------------------------------------------
//                       | BANK_N * 32b   
//                 ------------      
//                 | V bank_N |      
//                 ------------   

//=================================================================================
// Local Parameters
//=================================================================================
// Pre-arbiter fixed priority order: LSB > MSB.
localparam MUX_PARB_SBU = 0; //sbu
localparam MUX_PARB_SLV = 1; //slv
 
localparam MUX_VARB_VREQ = 0; //vpu
localparam MUX_VARB_LDXB = 1; //ldxb
localparam MUX_VARB_PARB = 2; //parb

//=================================================================================
// Local Variables 
//=================================================================================
logic                                   vtcm_busy; //vtcm的忙信号

logic [1:0][BANK_N-1:0]                 lsu_vbank_sel_oh; //lsu访问vtcm的bank选择
logic [BANK_N-1:0][1:0]                 ldxb_req; //ldxb的请求
logic [BANK_N-1:0][1:0]                 ldxb_grt; //ldxb的优先级
logic [BANK_N-1:0][19:0]                ldxb_varb_req_addr; //ldxb的地址
logic [BANK_N-1:0][ID_W-1:0]            ldxb_varb_req_id  ; //ldxb的id
logic [1:0][BANK_N-1:0]                 ldxb_req_rdy; //ldxb的请求响应
 
logic [1:0]                             parb_req; //parb的请求
logic [1:0][19:0]                       parb_req_addr; //parb的地址
logic [1:0][ID_W-1:0]                   parb_req_id  ; //parb的id
logic [1:0]                             parb_req_wr  ; //parb的写使能
logic [1:0][63:0]                       parb_req_wdata; //parb的数据
logic [1:0][7:0]                        parb_req_wstrb; //parb的写使能
logic [1:0]                             parb_grt; //parb的优先级
logic                                   parb_varb_req_vld_pre  ; //parb的请求有效信号
logic [19:0]                            parb_varb_req_addr_pre ; //parb的地址
logic [ID_W-1:0]                        parb_varb_req_id_pre   ; //parb的id
logic                                   parb_varb_req_wr_pre   ; //parb的写使能
logic [63:0]                            parb_varb_req_wdata_pre; //parb的数据
logic [7:0]                             parb_varb_req_wstrb_pre; //parb的写使能
 
logic [BANK_N-1:0]                      parb_varb_req_vld; //parb的请求有效信号
logic [BANK_N-1:0]                      parb_varb_req_wr; //parb的写使能
logic [BANK_N-1:0][19:0]                parb_varb_req_addr; //parb的地址
logic [BANK_N-1:0][31:0]                parb_varb_req_wdata; //parb的数据
logic [BANK_N-1:0][3:0]                 parb_varb_req_wstrb; //parb的写使能
logic [BANK_N-1:0][1:0]                 parb_varb_req_dest; //parb的目的

logic                                   vpu_req_vld; //vpu的请求有效信号
logic [BANK_N-1:0][2:0][19:0]           varb_req_addr; //varb的地址
logic [BANK_N-1:0][2:0][ID_W-1:0]       varb_req_id; //varb的id
logic [BANK_N-1:0][2:0]                 varb_req_wr; //varb的写使能
logic [BANK_N-1:0][2:0][31:0]           varb_req_wdata; //varb的数据
logic [BANK_N-1:0][2:0][3:0]            varb_req_wstrb; //varb的写使能
logic [BANK_N-1:0][2:0][1:0]            varb_req_dest; //varb的目的
 
logic [2:0]                             varb_req; //varb的请求
logic [2:0]                             varb_grt; //varb的优先级
logic [BANK_N-1:0]                      varb_ldxb_req_rdy; //varb的ldxb请求响应
logic [BANK_N-1:0]                      varb_parb_req_rdy; //varb的parb请求响应
 
logic [BANK_N-1:0][31:0]                vbank_wdata_pre; //vbank的数据
logic [BANK_N-1:0][6:0]                 vbank_wdata_code; //vbank的数据
 
assign vtcm_busy = |bank_busy; //vtcm的忙信号

//=================================================================================
// ld-xbar
//=================================================================================
// LSU load req routed by vtcu_bank_judge
// 将双发射load请求交换为DTCM低bank/高bank请求
vtcu_bank_judge #(.BANK_N(BANK_N), .BANK_DATA_W(32), .IN_ADDR_W(20), .MODE(2'b00)) u_ldxb_bank_judge_0(.i_addr(lsu_vtcm_req_addr[0]), .o_bank_sel_oh (lsu_vbank_sel_oh[0])); //通过LSU0的地址，判断返回低bank/高bank请求
vtcu_bank_judge #(.BANK_N(BANK_N), .BANK_DATA_W(32), .IN_ADDR_W(20), .MODE(2'b00)) u_ldxb_bank_judge_1(.i_addr(lsu_vtcm_req_addr[1]), .o_bank_sel_oh (lsu_vbank_sel_oh[1])); //通过LSU1的地址，判断返回低bank/高bank请求

for (genvar i=0; i<BANK_N; i++) begin: g_ldxb
  //Bank选择逻辑：根据LSU请求地址的低2位选择返回低bank/高bank请求
  assign ldxb_req[i][0] = lsu_vtcm_req_vld[0] & lsu_vbank_sel_oh[0][i]; //选择每一个bank的低bank请求
  assign ldxb_req[i][1] = lsu_vtcm_req_vld[1] & lsu_vbank_sel_oh[1][i]; //选择每一个bank的高bank请求

  wing_cbb_priority_queue #(.MODE(0), .WIDTH(2), .LOCK(0)) u_ff1_ldxb (.clk(clk), .rst_n(rst_n), .lock(1'b0), .vec_i(ldxb_req[i]), .onehot_o(ldxb_grt[i])); //优先级队列，选择最高优先级的请求

  wing_cbb_aomux #(.NUM(2), .WIDTH(20  )) u_aomux_ldxb_addr (.data_arr(lsu_vtcm_req_addr), .sel_in_onehot0(ldxb_grt[i]), .data_sel(ldxb_varb_req_addr[i])); //选择最高优先级的请求的地址
  wing_cbb_aomux #(.NUM(2), .WIDTH(ID_W)) u_aomux_ldxb_id   (.data_arr(lsu_vtcm_req_id  ), .sel_in_onehot0(ldxb_grt[i]), .data_sel(ldxb_varb_req_id[i]  )); //选择最高优先级的请求的id

  assign ldxb_req_rdy[0][i] = ldxb_grt[i][0] & varb_ldxb_req_rdy[i]; //根据优先级选择最高优先级的请求的响应
  assign ldxb_req_rdy[1][i] = ldxb_grt[i][1] & varb_ldxb_req_rdy[i]; //根据优先级选择最高优先级的请求的响应
 
end

assign vtcm_lsu_req_rdy[0] = (|ldxb_req_rdy[0]); //返回LSU0的响应
assign vtcm_lsu_req_rdy[1] = (|ldxb_req_rdy[1]); //返回LSU1的响应

//=================================================================================
// pre-arbiter
//=================================================================================
assign parb_req      [MUX_PARB_SBU] = sbu_vtcm_req_vld; //sbu的请求有效信号
assign parb_req      [MUX_PARB_SLV] = slv_vtcm_req_vld; //slv的请求有效信号
 
assign parb_req_addr [MUX_PARB_SBU] = sbu_vtcm_req_addr; //sbu的地址
assign parb_req_addr [MUX_PARB_SLV] = slv_vtcm_req_addr; //slv的地址

assign parb_req_id   [MUX_PARB_SBU] = sbu_vtcm_req_id; //sbu的id
assign parb_req_id   [MUX_PARB_SLV] = slv_vtcm_req_id; //slv的id
 
assign parb_req_wr   [MUX_PARB_SBU] = 1'b1; //sbu的写使能
assign parb_req_wr   [MUX_PARB_SLV] = slv_vtcm_req_wr; //slv的写使能

assign parb_req_wdata[MUX_PARB_SBU] = sbu_vtcm_req_wdata; //sbu的数据
assign parb_req_wdata[MUX_PARB_SLV] = slv_vtcm_req_wdata; //slv的数据
 
assign parb_req_wstrb[MUX_PARB_SBU] = sbu_vtcm_req_wstrb; //sbu的写使能
assign parb_req_wstrb[MUX_PARB_SLV] = slv_vtcm_req_wstrb; //slv的写使能

// SBU > SLV
wing_cbb_priority_queue #(.MODE(0), .WIDTH(2), .LOCK(0)) u_ff1_parb (.clk(clk), .rst_n(rst_n), .lock(1'b0), .vec_i(parb_req), .onehot_o(parb_grt)); //优先级队列，选择最高优先级的请求
 
assign parb_varb_req_vld_pre = (|parb_req) & ~vtcm_busy; //parb的请求有效信号
 
wing_cbb_aomux #(.NUM(2), .WIDTH(20     )) u_aomux_parb_addr (.data_arr(parb_req_addr ), .sel_in_onehot0(parb_grt), .data_sel(parb_varb_req_addr_pre )); //选择最高优先级的请求的地址
wing_cbb_aomux #(.NUM(2), .WIDTH(ID_W   )) u_aomux_parb_id   (.data_arr(parb_req_id   ), .sel_in_onehot0(parb_grt), .data_sel(parb_varb_req_id_pre   )); //选择最高优先级的请求的id
wing_cbb_aomux #(.NUM(2), .WIDTH(1      )) u_aomux_parb_wr   (.data_arr(parb_req_wr   ), .sel_in_onehot0(parb_grt), .data_sel(parb_varb_req_wr_pre   )); //选择最高优先级的请求的写使能
wing_cbb_aomux #(.NUM(2), .WIDTH(64     )) u_aomux_parb_wdata(.data_arr(parb_req_wdata), .sel_in_onehot0(parb_grt), .data_sel(parb_varb_req_wdata_pre)); //选择最高优先级的请求的数据
wing_cbb_aomux #(.NUM(2), .WIDTH(8      )) u_aomux_parb_wstrb(.data_arr(parb_req_wstrb), .sel_in_onehot0(parb_grt), .data_sel(parb_varb_req_wstrb_pre)); //选择最高优先级的请求的写使能


//把一条宽请求拆分并路由：将一条 DATA_W 位的原始请求按 BANK_DATA_W 等宽分成 N=DATA_W/BANK_DATA_W 段，按地址中的 ban// SBU and SLV req routed by vtcu_bank_router_scatterk 字段与段序号的关系，分发到对应的 BANK_N 个 VTCM bank。
//为读回重排提供指示：为每个 bank 生成一组 N 位的目的 one-hot（req_rdata_dest_oh[i]），用于后续把各 bank 的读结果拼回原始宽数据的正确段位置。
//模式控制：仅在 MODE==2'b00 时工作；其他模式所有输出清零。
vtcu_bank_router_scatter #(
  .BANK_N       ( BANK_N ),
  .BANK_DATA_W  ( 32     ),
  .DATA_W       ( 64     )
) u_slv_router_scatter (
  .origin_vld        ( parb_varb_req_vld_pre   ), //parb的请求有效信号
  .origin_wr         ( parb_varb_req_wr_pre    ), //parb的写使能
  .origin_addr       ( parb_varb_req_addr_pre  ), //parb的地址
  .origin_wdata      ( parb_varb_req_wdata_pre ), //parb的数据
  .origin_wstrb      ( parb_varb_req_wstrb_pre ), //parb的写使能
  .req_vld           ( parb_varb_req_vld       ), //输出n个parb的请求有效信号
  .req_wr            ( parb_varb_req_wr        ), //输出n个parb的写使能
  .req_addr          ( parb_varb_req_addr      ), //输出n个parb的地址
  .req_wdata         ( parb_varb_req_wdata     ), //输出n个parb的数据
  .req_wstrb         ( parb_varb_req_wstrb     ), //输出n个parb的写使能
  .req_rdata_dest_oh ( parb_varb_req_dest      ) //输出n个parb的目的
);

 
assign vtcm_sbu_req_rdy = parb_grt[MUX_PARB_SBU] & (|varb_parb_req_rdy); //返回sbu的响应
assign vtcm_slv_req_rdy = parb_grt[MUX_PARB_SLV] & (|varb_parb_req_rdy); //返回slv的响应

//=================================================================================
// V REQ
//=================================================================================
// VPU req routed directly
assign vpu_req_vld = (|vpu_vtcm_req_vld) & ~vtcm_busy; //vpu的请求有效信号由  vpu0/vpu1/vpu n的请求 && vtcm中的m2或者m3有自写
 
//=================================================================================
// VTCM Arbiter
//=================================================================================

for (genvar i=0; i<BANK_N; i++) begin: g_vxb_req_info
 
  // v-req
  assign varb_req_addr [i][MUX_VARB_VREQ] = vpu_vtcm_req_addr[i] ; //v-arb仲裁的地址
  assign varb_req_id   [i][MUX_VARB_VREQ] = vpu_vtcm_req_id[i]   ; //v-arb仲裁的id
  assign varb_req_wr   [i][MUX_VARB_VREQ] = vpu_vtcm_req_wr[i]   ; //v-arb仲裁的写使能
  assign varb_req_wdata[i][MUX_VARB_VREQ] = vpu_vtcm_req_wdata[i]; //v-arb仲裁的数据
  assign varb_req_wstrb[i][MUX_VARB_VREQ] = vpu_vtcm_req_wstrb[i]; //v-arb仲裁的写使能
  assign varb_req_dest [i][MUX_VARB_VREQ] = 2'b0; 
 
  // ldxb
  assign varb_req_addr [i][MUX_VARB_LDXB] = ldxb_varb_req_addr[i];//ldxb-arb仲裁的地址
  assign varb_req_id   [i][MUX_VARB_LDXB] = ldxb_varb_req_id[i]  ;//ldxb-arb仲裁的id
  assign varb_req_wr   [i][MUX_VARB_LDXB] = 1'b0 ;//ldxb-arb仲裁的写使能
  assign varb_req_wdata[i][MUX_VARB_LDXB] = 32'b0;//ldxb-arb仲裁的数据
  assign varb_req_wstrb[i][MUX_VARB_LDXB] = 4'b0 ;//ldxb-arb仲裁的写使能
  assign varb_req_dest [i][MUX_VARB_LDXB] = 2'b0 ;//ldxb-arb仲裁的目的
 
  // pre-arb
  assign varb_req_addr [i][MUX_VARB_PARB] = parb_varb_req_addr[i] ;//parb-arb仲裁的地址
  assign varb_req_id   [i][MUX_VARB_PARB] = parb_varb_req_id_pre  ;//parb-arb仲裁的id
  assign varb_req_wr   [i][MUX_VARB_PARB] = parb_varb_req_wr[i]   ;//parb-arb仲裁的写使能
  assign varb_req_wdata[i][MUX_VARB_PARB] = parb_varb_req_wdata[i];//parb-arb仲裁的数据
  assign varb_req_wstrb[i][MUX_VARB_PARB] = parb_varb_req_wstrb[i];//parb-arb仲裁的写使能
  assign varb_req_dest [i][MUX_VARB_PARB] = parb_varb_req_dest[i] ;//parb-arb仲裁的目的
 
end
 
assign varb_req[MUX_VARB_VREQ] = vpu_req_vld; //vpu的请求有效信号
assign varb_req[MUX_VARB_LDXB] = |ldxb_grt; //ldxb的仲裁请求有效信号
assign varb_req[MUX_VARB_PARB] = |parb_varb_req_vld; //parb的仲裁请求有效信号

// Fixed priority arbiter.
// vpu > load > parb
assign varb_grt[MUX_VARB_VREQ] = varb_req[MUX_VARB_VREQ]; //vpu的优先级
assign varb_grt[MUX_VARB_LDXB] = varb_req[MUX_VARB_LDXB] & ~varb_req[MUX_VARB_VREQ]; //ldxb的优先级
assign varb_grt[MUX_VARB_PARB] = varb_req[MUX_VARB_PARB] & ~varb_req[MUX_VARB_VREQ] & ~varb_req[MUX_VARB_LDXB]; //parb的优先级
  
for (genvar i=0; i<BANK_N; i++) begin: g_vxb_rsp_rdy

  assign varb_ldxb_req_rdy[i] = varb_grt[MUX_VARB_LDXB] & vbank_rdy[i]; //ldxb的请求响应
  assign varb_parb_req_rdy[i] = varb_grt[MUX_VARB_PARB] & vbank_rdy[i]; //parb的请求响应
  assign vtcm_vpu_req_rdy[i]  = varb_grt[MUX_VARB_VREQ] & vbank_rdy[i]; //vpu的请求响应
 
end
 
for (genvar i=0; i<BANK_N; i++) begin: g_vxb_req_mux

  assign vbank_vld[i] = varb_grt[MUX_VARB_VREQ] & vpu_vtcm_req_vld[i] | //vpu的请求有效信号  ||
                        varb_grt[MUX_VARB_LDXB] & (|ldxb_req[i])      | //ldxb的请求有效信号 ||
                        varb_grt[MUX_VARB_PARB] & parb_varb_req_vld[i]; //parb的请求有效信号

  wing_cbb_aomux #(.NUM(3), .WIDTH(20  )) u_aomux_varb_addr  (.data_arr(varb_req_addr[i] ), .sel_in_onehot0(varb_grt), .data_sel(vbank_addr[i]     )); //选择最高优先级的请求的地址
  wing_cbb_aomux #(.NUM(3), .WIDTH(ID_W)) u_aomux_varb_id    (.data_arr(varb_req_id[i]   ), .sel_in_onehot0(varb_grt), .data_sel(vbank_id[i]       )); //选择最高优先级的请求的id
  wing_cbb_aomux #(.NUM(3), .WIDTH(1   )) u_aomux_varb_wr    (.data_arr(varb_req_wr[i]   ), .sel_in_onehot0(varb_grt), .data_sel(vbank_wr[i]       )); //选择最高优先级的请求的写使能
  wing_cbb_aomux #(.NUM(3), .WIDTH(32  )) u_aomux_varb_wdata (.data_arr(varb_req_wdata[i]), .sel_in_onehot0(varb_grt), .data_sel(vbank_wdata_pre[i])); //选择最高优先级的请求的数据
  wing_cbb_aomux #(.NUM(3), .WIDTH(4   )) u_aomux_varb_wstrb (.data_arr(varb_req_wstrb[i]), .sel_in_onehot0(varb_grt), .data_sel(vbank_wstrb[i]    )); //选择最高优先级的请求的写使能
  wing_cbb_aomux #(.NUM(3), .WIDTH(2   )) u_aomux_varb_dest  (.data_arr(varb_req_dest[i] ), .sel_in_onehot0(varb_grt), .data_sel(vbank_dest[i]     )); //选择最高优先级的请求的目的

end

//=================================================================================
// ECC Encode (if configured)
//=================================================================================
// Encode data after arbiter and before vtcm_bank
for (genvar i=0; i<BANK_N; i++) begin: g_ecc_enc
 
  wing_cbb_ecc_enc32 u_varb_enc_32(.data_i(vbank_wdata_pre[i]), .enc_o(vbank_wdata_code[i])); //将写数据进行ecc编码

`ifdef CHUNJUN_SUPPORT_MEM_ECC
  assign vbank_wdata[i] = {vbank_wdata_code[i], vbank_wdata_pre[i]}; // width = 39 //将ecc编码后的数据和原始数据拼接
`else  // CHUNJUN_SUPPORT_MEM_ECC 
  assign vbank_wdata[i] = vbank_wdata_pre[i]; //不进行ecc编码
`endif // CHUNJUN_SUPPORT_MEM_ECC
 
end

//=================================================================================
// Assertion
//=================================================================================
 
 
endmodule
`include "chunjun_undefine.sv" 
